package agent

import (
	"encoding/json"
	"fmt"
	"os"
	"runtime"
	"strings"
	"text/template"
	"time"
)

// SystemPromptTemplate 系统提示模板内容
const SystemPromptTemplate = `

你是一个ReAct (Reasoning + Acting) 智能体，能够通过交替进行推理和行动来解决问题。

**环境信息:**
- 操作系统: {{.OSInfo}}
- 当前目录: {{.CurrentDir}}
- 当前时间: {{.CurrentTime}}

**角色定义:**
你是一个专业的AI助手，具备通过思考和行动解决复杂问题的能力。你遵循ReAct模式，通过思考(Thought)→行动(Action)→观察(Observation)的循环来逐步解决问题。

**可用工具:**
{{.ToolsDescription}}

**输出格式要求:**
你必须严格按照以下JSON格式输出你的下一步决策，用markdown代码块包裹：

**JSON格式要求:**
1. 响应必须用markdown代码块标记，使用 ` + "```json" + ` 开始， ` + "```" + ` 结束
2. 代码块内部必须是有效的JSON对象
3. 确保JSON结构完整，没有语法错误
4. 使用标准双引号(")，不是中文引号

如果决定思考:
` + "```json" + `
{"type":"thought","content":"你的推理过程","reasoning":"你为什么这样思考"}
` + "```" + `

如果决定行动:
` + "```json" + `
{"type":"action","action":"工具名称","parameters":{"参数名":"参数值"},"reasoning":"为什么选择这个行动"}
` + "```" + `

如果需要询问用户以获取更多信息:
` + "```json" + `
{"type":"ask_user","question":"需要问用户的具体问题","reasoning":"为什么需要这个信息"}
` + "```" + `

如果认为问题已解决:
` + "```json" + `
{"type":"completed","content":"简短总结解决方案","final_answer":"直接回答用户的问题"}
` + "```" + `

**重要提醒:**
- 必须用 ` + "```json" + ` 和 ` + "```" + ` 包裹JSON内容
- 违反格式将导致解析失败
- 不要包含反引号在JSON内容中

**ReAct循环规则:**
1. 思考(Thought): 分析问题并决定下一步行动
2. 行动(Action): 使用一个可用工具
3. 观察(Observation): 分析行动的结果
4. 重复这个过程直到问题解决或达到最大循环次数

**重要约束:**
- 每个思考都应该推动问题解决
- 每个行动都必须使用一个可用工具
- 行动参数必须具体、有效
- 如果行动失败，分析原因并调整策略
- 涉及读写文件时，优先使用已有的文件读写工具，而不是用执行命令方式读写文件
- 读取文件前应该先确认文件总行数，超出100行分段读取，防止上下文爆炸问题
- 如果用户的问题超出了你现有的知识范围，或需要最新信息时，请优先使用web_search工具进行网络搜索，获取实时准确的信息。该工具使用智能搜索引擎，能够提供即时答案、摘要信息和相关链接，支持中英文查询。
- 最多进行 {{.MaxIterations}} 个循环

**何时使用ask_user:**
- 用户指令模糊不清，存在多种理解方式
- 需要用户提供具体的参数或选项（如文件路径、配置值等）
- 执行可能有风险的操作前需要用户确认具体细节
- 需要了解用户的偏好或要求
- 遇到多种可行方案需要用户选择
`

// PromptBuilder 负责构建ReAct提示词
type PromptBuilder struct {
	agent          *ReActAgent
	systemTemplate *template.Template
}

// NewPromptBuilder 创建新的提示词构建器
func NewPromptBuilder(agent *ReActAgent) (*PromptBuilder, error) {
	// 使用内嵌的系统提示模板
	systemTemplate, err := template.New("system_prompt").Parse(SystemPromptTemplate)
	if err != nil {
		return nil, fmt.Errorf("解析系统提示模板失败: %v", err)
	}

	return &PromptBuilder{
		agent:          agent,
		systemTemplate: systemTemplate,
	}, nil
}

// PromptTemplateData 用于模板渲染的数据结构
type PromptTemplateData struct {
	Goal             string
	OSInfo           string
	CurrentDir       string
	CurrentTime      string
	ToolsDescription string
	HistoryText      string
	MaxIterations    int
}

// BuildSystemPrompt 构建纯净的系统提示词（不包含历史信息）
func (pb *PromptBuilder) BuildSystemPrompt() (string, error) {
	// 准备系统模板数据
	currentDir, _ := os.Getwd()
	currentTime := time.Now().Format("2006-01-02 15:04:05")
	osInfo := runtime.GOOS

	data := PromptTemplateData{
		OSInfo:           osInfo,
		CurrentDir:       currentDir,
		CurrentTime:      currentTime,
		ToolsDescription: pb.agent.GetToolsDescription(),
		MaxIterations:    pb.agent.GetMaxIterations(),
	}

	// 渲染系统提示模板
	var result strings.Builder
	err := pb.systemTemplate.Execute(&result, data)
	if err != nil {
		return "", fmt.Errorf("渲染系统提示模板失败: %v", err)
	}

	return result.String(), nil
}

// BuildHistoryText 构建历史步骤文本
func (pb *PromptBuilder) BuildHistoryText(session *ReActSession) string {
	historyText := ""
	for _, step := range session.GetSteps() {
		switch StepType(step.Type) {
		case StepTypeThought:
			historyText += fmt.Sprintf("思考 %d: %s\n", step.ID, step.Content)
		case StepTypeAction:
			paramsStr := ""
			if step.Parameters != nil {
				paramsBytes, _ := json.Marshal(step.Parameters)
				paramsStr = string(paramsBytes)
			}
			historyText += fmt.Sprintf("行动 %d: %s(%s)\n", step.ID, step.Action, paramsStr)
		case StepTypeObservation:
			historyText += fmt.Sprintf("观察 %d: %s\n", step.ID, step.Content)
			if step.Error != "" {
				historyText += fmt.Sprintf("错误: %s\n", step.Error)
			}
		case StepTypeAskUser:
			historyText += fmt.Sprintf("询问 %d: %s\n", step.ID, step.Question)
		case StepTypeUserAnswer:
			historyText += fmt.Sprintf("用户回答 %d: %s\n", step.ID, step.UserAnswer)
		}
	}
	return historyText
}

// // BuildReActPrompt 构建简化的ReAct推理提示词
// func (pb *PromptBuilder) BuildReActPrompt(session *ReActSession) (string, error) {
// 	historyText := pb.BuildHistoryText(session)

// 	if historyText != "" {
// 		// 如果有执行历史，基于历史决定下一步
// 		return fmt.Sprintf("**当前执行历史:**\n%s\n基于以上执行历史，请决定下一步该做什么。\n\n**重要格式提醒:**\n- 必须用markdown代码块包裹JSON，格式：```json ... ```\n- JSON内容必须是有效的对象格式\n- 不要包含反引号在JSON内容中\n\n注意：\n- 如果之前的行动失败了，请分析失败原因并调整策略\n- 如果任务已经完成，请返回completed类型的响应\n- 如果需要继续执行，请选择适当的工具继续任务", historyText), nil
// 	} else {
// 		// 任务开始
// 		return "**任务开始**\n\n请分析任务目标并开始执行。\n\n**重要格式提醒:**\n- 必须用markdown代码块包裹JSON，格式：```json ... ```\n- JSON内容必须是有效的对象格式\n- 不要包含反引号在JSON内容中", nil
// 	}
// }

// ResponseType 定义响应类型常量
type ResponseType string

const (
	ResponseTypeThought   ResponseType = "thought"
	ResponseTypeAction    ResponseType = "action"
	ResponseTypeAskUser   ResponseType = "ask_user"
	ResponseTypeCompleted ResponseType = "completed"
)

// Response 表示AI响应的结构
type Response struct {
	Type        ResponseType           `json:"type"`
	Content     string                 `json:"content,omitempty"`
	Action      string                 `json:"action,omitempty"`
	Parameters  map[string]interface{} `json:"parameters,omitempty"`
	Reasoning   string                 `json:"reasoning,omitempty"`
	Question    string                 `json:"question,omitempty"`
	FinalAnswer string                 `json:"final_answer,omitempty"`
}

// ParseResponse 解析AI响应
func (pb *PromptBuilder) ParseResponse(response map[string]interface{}) (*Response, error) {
	respType, ok := response["type"].(string)
	if !ok {
		return nil, fmt.Errorf("响应格式错误: 缺少type字段")
	}

	resp := &Response{
		Type: ResponseType(respType),
	}

	if content, ok := response["content"].(string); ok {
		resp.Content = content
	}
	if reasoning, ok := response["reasoning"].(string); ok {
		resp.Reasoning = reasoning
	}
	if action, ok := response["action"].(string); ok {
		resp.Action = action
	}
	if params, ok := response["parameters"].(map[string]interface{}); ok {
		resp.Parameters = params
	}
	if finalAnswer, ok := response["final_answer"].(string); ok {
		resp.FinalAnswer = finalAnswer
	}
	if question, ok := response["question"].(string); ok {
		resp.Question = question
	}

	return resp, nil
}
